import datetime
import json
import time

import requests
from dateutil.relativedelta import relativedelta

from django.utils import timezone  # 导入 timezone
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import PasswordChangeForm
from django.core.paginator import Paginator, PageNotAnInteger, InvalidPage, EmptyPage
from django.db import connection
from django.db.models import Q, Count
from django.http import HttpResponse, FileResponse
from django.shortcuts import render, redirect
from django.utils.encoding import escape_uri_path

from .forms import UserCustomRegister, UserCustomChange
from .models import *

# 导入封装好的分页组件
from utils.pagination import Pagination

from django.contrib import messages

# 总的登录界面
def loginOfAll(request):
    return render(request, 'loginOfAll.html')


# 首页，显示药品列表
def index(request):
    if request.method == 'GET':
        # 获取药品信息表的所有数据
        MedicineList = Medicine.objects.all()
        # 获取实验室信息表的所有信息
        LabList = Lab.objects.all()
        # 分页展示
        paginator = Paginator(MedicineList, 8)
        # 分页获取 url 后面的 page 参数的值, 首页不显示 page 参数, 默认值是 1
        page = request.GET.get('page')
        medicineList = paginator.get_page(page)
        content = {
            'medicineList': medicineList,  # 药品信息
            'LabList': LabList,  # 实验室信息
            'currentUser': request.user,  # 用户信息
        }
        return render(request, 'index.html', content)


# 实验室列表信息
def lab(request):
    data_dict = {}
    # 获取不到name值时默认查询条件为空
    search_data = request.GET.get('name', "")
    if search_data:
        # name__contains name字段模糊查询
        data_dict["name__contains"] = search_data

    # 获取查询模糊查询到的数据
    queryset = Lab.objects.filter(**data_dict)

    # 设置每页显示多少条数据
    page_size = 5
    # 调用封装好的分页组件
    page_object = Pagination(request, queryset, page_size)
    # 分完页后当前页要显示的数据
    LabList = page_object.page_queryset
    # 返回前端分页的跳转按钮
    page_string = page_object.html()

    context = {
        "search_data": search_data,
        "labList": LabList,  # 分完页的数据
        "page_string": page_string  # 页码
    }
    # search_data将查询条件返回 这样搜索完后搜索框的查询条件依然保留着 不会刷新掉
    return render(request, 'lab.html', context)


# 资料下载界面，只接受get请求
def about(request):
    if request.method == 'GET':
        docList = Doc.objects.all()  # 文件信息
        return render(request, 'about.html', locals())


# 搜索药品信息
def medicineSearch(request):
    LabList = Lab.objects.all()  # 实验室信息
    if request.method == 'POST':
        searchType = request.POST['searchType']  # 按照什么搜索
        keyword = request.POST['keyword']  # 搜索内容
        # 按照名字搜索
        if searchType == 'name':
            # name__contains 对药品名字进行模糊查询
            MedicineList = Medicine.objects.filter(name__contains=keyword)
        # 按照编号搜索
        elif searchType == 'number':
            # number__contains 对药品编号进行模糊查询
            MedicineList = Medicine.objects.filter(number__contains=keyword)
        # 按照全部搜索
        else:
            # 对药品名字或者药品编号进行模糊查询
            MedicineList = Medicine.objects.filter(Q(name__contains=keyword) |
                                                   Q(number__contains=keyword))
        # 将数据按照规定每页显示 8 条, 进行分割
        paginator = Paginator(MedicineList, 8)
        # 获取 url 后面的 page 参数的值, 首页不显示 page 参数, 默认值是 1
        page = request.GET.get('page')
        # 分页后的数据
        medicineList = paginator.get_page(page)
        content = {'medicineList': medicineList,
                   'LabList': LabList,
                   'currentUser': request.user
                   }
        return render(request, 'index.html', content)

from django.http import HttpResponseRedirect

import tkinter.messagebox #弹窗库
from tkinter import *
# 登陆
def loging(request):
    if request.method == 'POST':
        # authenticate是Django提供的一个函数，用于验证用户名和密码是否匹配。
        user = authenticate(request, username=request.POST['username'], password=request.POST['password'])
        if user is None:
            messages.error(request, '用户不存在或密码错误，请检查！！')
            return HttpResponseRedirect('/login/')

        else:
            login(request, user)
            messages.success(request, '登录成功！')
            return redirect('Lab:index')
    else:
        return render(request, 'login.html')


# 退出登录
def logouting(request):
    logout(request)
    # messages.error(request, '退出登录成功！')
    messages.success(request, '退出登录成功！')
    # tkinter.messagebox.showinfo('提示', "退出登录成功！")
    return redirect('Lab:index')


# 注册
def register(request):
    if request.method == 'POST':
        # 获取用户POST提交的表单所有信息
        registerForm = UserCustomRegister(request.POST)
        # registerForm.is_valid():对数据字段进行逐一校验
        if registerForm.is_valid():
            """
            自动保存到数据库auth_user表里面
            只会保存User模型中的字段（即username, password和email）
            """
            registerForm.save()
            """
            registerForm.cleaned_data校验成功获取数据
            registerForm.cleaned_data['username']校验成功获取指定username字段的数据
            """
            # 使用authenticate方法验证用户名和密码是否匹配，并获取用户对象
            newUser = authenticate(username=registerForm.cleaned_data['username'],
                                   password=registerForm.cleaned_data['password1'])
            newUser.email = registerForm.cleaned_data['email']
            # 自动保存到数据库common_user表里面
            CommonUser(user=newUser, nickyName=registerForm.cleaned_data['nickyName'],
                       cate=registerForm.cleaned_data['cate'],
                       faculty=registerForm.cleaned_data['faculty']).save()
            # 使用login方法将用户登录到会话中。
            # login(request, newUser)
            messages.success(request,"注册用户成功!")
            return redirect('Lab:login')
    else:
        registerForm = UserCustomRegister()
    content = {'registerForm': registerForm}
    return render(request, 'register.html', content)


# 用户查看个人信息，需要先登录
@login_required(login_url='Lab:login')
def userCentre(request):
    # 修改个人信息
    if request.method == 'POST':
        changeForm = UserCustomChange(request.POST, instance=request.user)
        if changeForm.is_valid():
            changeForm.save()
            request.user.commonuser.nickyName = changeForm.cleaned_data['nickyName']
            request.user.commonuser.save()
            messages.success(request,'修改个人信息成功!')
    else:
        pass
    changeForm = UserCustomChange()
    content = {'currentUser': request.user, 'changeForm': changeForm}
    return render(request, 'userCentre.html', content)


# 用户修改密码
@login_required(login_url='Lab:login')
def changePassword(request):
    if request.method == 'POST':
        # 调用Django内置的PasswordChangeForm表单，将请求中的数据和当前用户作为参数传递给该表单。
        # 表单会验证用户输入的新密码是否符合要求，并检查旧密码是否匹配。
        changepasswordForm = PasswordChangeForm(data=request.POST, user=request.user)
        if changepasswordForm.is_valid():
            changepasswordForm.save()
            messages.success(request,"修改密码成功！")
            return redirect('Lab:login')
        messages.error(request, "修改密码失败！")
        return redirect('Lab:changePassword')
    else:
        pass
    changepasswordForm = PasswordChangeForm(user=request.user)
    content = {
        'currentUser': request.user,
        'changepasswordForm': changepasswordForm
    }
    return render(request, 'changePassword.html', content)


# 学生借用药品历史查看
@login_required(login_url='Lab:login')
def borrowHistory(request):
    if request.method == 'GET':
        user = CommonUser.objects.filter(user=request.user).first()
        # 获取查询模糊查询到的数据
        queryset = Borrow.objects.filter(user=user).order_by("-boDate")

        # 设置每页显示多少条数据
        page_size = 10
        # 调用封装好的分页组件
        page_object = Pagination(request, queryset, page_size)
        # 分完页后当前页要显示的数据
        MedicineListTarget = page_object.page_queryset
        # 返回前端分页的跳转按钮
        page_string = page_object.html()

        MedicineList = []
        today = timezone.now()  # 使用 Django 的 timezone.now() 获取带时区的当前时间

        for borrowInfo in MedicineListTarget:
            if borrowInfo.boDate:
                borrowTime = (today - borrowInfo.boDate).days  # 计算完整的天数差
                borrowTime = max(borrowTime, 0)  # 防止负数
            else:
                borrowTime = 0  # 如果 boDate 为空，默认 0 天

            # borrowTime = datetime.datetime.now().day - borrowInfo.boDate.day  # 已经借用的天数

            MedicineList.append((Medicine.objects.get(pk=borrowInfo.medicine_id), borrowInfo, borrowTime))
        context = {
            'MedicineList': MedicineList,
            'currentUser': request.user,
            "page_string": page_string  # 页码
        }

        return render(request, 'borrowHistory.html', context)


# 药品详情
def medicineDetail(request, medicineId):
    if request.method == 'GET':
        thisMedicine = Medicine.objects.get(id=medicineId)
        thisLab = Lab.objects.all()
        content = {
            'medicine': thisMedicine,
            'thisLab': thisLab
        }
        return render(request, 'detail.html', content)


# 借用药品
@login_required(login_url='Lab:login')
def subAndBo(request, medicine_id):
    if request.method == 'POST':
        Lab_id = request.POST.get('lab_id')  # 获取表单传过来 借用的实验室id
        thisNum = int(request.POST.get('medicineUsedNum'))  # 药品编号
        thisUser = User.objects.get(pk=request.user.id)  # 取用用户
        # p判断实验室是否存在
        labTrue = Lab.objects.filter(id=Lab_id).count()
        thisMedicine = Medicine.objects.get(pk=medicine_id)
        # 取用的数量大于库存数量
        if thisMedicine.nowtotal < thisNum:
            messages.error(request, '借用失败！库存不足啦！')
            return redirect('Lab:index')
        # 不存在填写的实验室名称
        elif labTrue == 0:
            messages.error(request, '您输入的实验室不存在')
            return redirect('Lab:index')
        # 满足条件可以取用
        else:
            thisMedicine.usedtotal += thisNum
            thisMedicine.nowtotal -= thisNum
            thisMedicine.save()
            newBorrow = Borrow(user=thisUser.commonuser, lab_id=Lab_id,
                               medicine=thisMedicine, medicineUsedNum=thisNum)
            newBorrow.save()
            messages.success(request, '借用药品成功!')
            return redirect('Lab:index')


# 用户信息界面
def userInfo(request):
    return render(request, 'usr_info.html')


# 仪器列表
def ins_index(request):
    if request.method == 'GET':
        data_dict = {}
        # 获取不到name值时默认查询条件为空
        search_data = request.GET.get('ins_name', "")
        if search_data:
            # ins_name__contains ins_name字段模糊查询
            data_dict["ins_name__contains"] = search_data

        # 获取查询模糊查询到的数据
        queryset = Instrument.objects.filter(**data_dict)

        # 设置每页显示多少条数据
        page_size = 10
        # 调用封装好的分页组件
        page_object = Pagination(request, queryset, page_size)
        # 分完页后当前页要显示的数据
        instrumentList = page_object.page_queryset
        # 返回前端分页的跳转按钮
        page_string = page_object.html()

        content = {
            "search_data": search_data,
            "instrumentList": instrumentList,  # 分完页的数据
            'currentUser': request.user,
            "page_string": page_string  # 页码
        }
        # search_data将查询条件返回 这样搜索完后搜索框的查询条件依然保留着 不会刷新掉
        return render(request, 'ins_index.html', content)


# 仪器借用
@login_required(login_url='Lab:login')
def ins_subAndBo(request, ins_id):
    if request.method == 'POST':
        thisNum = int(request.POST.get('insUsedNum'))  # 仪器数量
        thisUser = User.objects.get(pk=request.user.id)  # 借用的用户
        thisInstrument = Instrument.objects.get(pk=ins_id)  # 仪器信息
        # 借用数量超过了库存数量
        if thisInstrument.ins_nowtotal < thisNum:
            messages.error(request,"借用失败！库存不足！")
            return redirect('Lab:ins_index')
        # 满足借用条件
        else:
            thisInstrument.ins_bototal += thisNum  # 借出数量增加
            thisInstrument.ins_nowtotal -= thisNum  # 剩余量减少
            thisInstrument.save()
            newBorrow = InsBorrow(ins_user=thisUser.commonuser,
                                  instrument=thisInstrument, ins_boNum=thisNum)
            newBorrow.save()
            messages.success(request, "借用成功！")
            return redirect('Lab:ins_index')


# 仪器归还
@login_required(login_url='Lab:login')
def ins_addAndTu(request, bor_id):
    if request.method == 'POST':
        thisNum = int(request.POST.get('insTurnedNum'))  # 仪器归还数量
        thisUser = User.objects.get(pk=request.user.id)  # 借用的用户
        thisInsbro = InsBorrow.objects.get(pk=bor_id)  # 获取借用信息
        thisInstrument = Instrument.objects.get(pk=thisInsbro.instrument_id)  # 通过外键仪器id查询仪器信息
        # 归还数量大于借用的数量
        if thisInsbro.ins_boNum < thisNum:
            messages.error(request, '归还失败！归还数量有误！')
            return redirect('Lab:borrowInsHistory')
        # 归还部分数量
        elif thisInsbro.ins_boNum > thisNum:
            thisInstrument.ins_bototal -= thisNum  # 借出数量减少
            thisInstrument.ins_nowtotal += thisNum  # 剩余量增加
            thisInstrument.save()
            thisInsbro.ins_boNum -= thisNum
            thisInsbro.save()
            messages.success(request, '归还部分仪器成功！')
            return redirect('Lab:borrowInsHistory')
        # 全部归还
        else:
            thisInstrument.ins_bototal -= thisNum  # 借出数量减少
            thisInstrument.ins_nowtotal += thisNum  # 剩余量增加
            thisInstrument.save()
            # 借用数量归0
            thisInsbro.ins_boNum = 0
            now = datetime.datetime.now()
            str_now = now.strftime("%Y-%m-%d %H:%M:%S")
            thisInsbro.ins_tuData = str_now
            thisInsbro.save()
            messages.success(request, '归还仪器成功！')
            return redirect('Lab:borrowInsHistory')


# 用户仪器借用历史记录
@login_required(login_url='Lab:login')
def borrowInsHistory(request):
    if request.method == 'GET':
        user = CommonUser.objects.filter(user=request.user).first()
        # order_by("-ins_boDate") 按照借用时间倒序排序
        queryset = InsBorrow.objects.filter(ins_user=user).order_by("-ins_boDate")

        # 设置每页显示多少条数据
        page_size = 10
        # 调用封装好的分页组件
        page_object = Pagination(request, queryset, page_size)
        # 分完页后当前页要显示的数据
        InstrumentListTarget = page_object.page_queryset
        # 返回前端分页的跳转按钮
        page_string = page_object.html()
        InstrumentList = []
        for borrowInfo in InstrumentListTarget:
            borrowTime = datetime.datetime.now().day - borrowInfo.ins_boDate.day
            InstrumentList.append((Instrument.objects.get(pk=borrowInfo.instrument.id), borrowInfo, borrowTime))
        content = {
            'InstrumentList': InstrumentList,
            'currentUser': request.user,
            "page_string":page_string, #页码
        }
        return render(request, 'ins_borrowHistory.html', content)


# 文件资料下载
def download_file(request, doc_id):
    # 获取资料信息用户显示
    if request.method == 'GET':
        file_result = Doc.objects.filter(id=doc_id)
        if file_result:
            # 从查询结果中获取第一个文件对象。
            file = list(file_result)[0]
            # 设置文件在服务器上的路径，下载连接
            ip = 'http://127.0.0.1:8000/media/'
            # 将基路径与file文件夹内的文件名字拼接，形成完整的文件路径。
            path = ip + str(file.file_obj)
            # 下载资料
            try:
                # 使用requests库发起GET请求，通过浏览器下载
                response = FileResponse(requests.get(path, stream=True))
            except requests.exceptions.ConnectionError as e:
                response.status_code = '链接请求失败'
            # 从文件路径中提取文件扩展名
            file_ext = path.split(".")[-1]
            if not file_ext:
                pass
            else:
                # 将文件扩展名转换为小写
                file_ext = file_ext.lower()

            # 设置响应的Content - Type为application / octet - stream，表示二进制流。
            response['Content-Type'] = 'application/octet-stream'
            # 获取文件名
            FileName = escape_uri_path(path).split('/')[-1]
            # 设置响应的头部以指示浏览器这是一个应该下载的文件，并设置其文件名。
            response['Content-Disposition'] = 'attachment;filename="{0}"'.format(FileName)
            # 返回响应对象
            return response
        else:
            return HttpResponse("文件不存在")




# 导入pandas数据分析库
import pandas as pd


# 查询两个时间段之间的药品借用数据方法
def getData(time1, time2):
    # 创建一个新的数据库游标对象。游标用于执行查询和获取结果。
    cursor = connection.cursor()
    sql1 = ''' select  name ,sum(medicineUsedNum)  from borrow, medicine 
    where borrow.medicine_id=medicine.id and boDate between %s and %s group by medicine.id'''
    # cursor.execute执行SQL查询
    cursor.execute(sql1, [time1, time2])
    # 将cursor查询到的结果存储在变量data4
    data4 = cursor.fetchall()
    # 将data4列表转换为pandas的DataFrame对象
    data4 = pd.DataFrame(list(data4), columns=['药品', '使用量'])
    # 关闭游标，释放与游标相关的资源。
    cursor.close()
    return data4  # 返回表格数据


# 查询近一月有借用纪录的药品的名字和使用风险度
def getRisk():
    # 创建一个新的数据库游标对象。游标用于执行查询和获取结果。
    cursor = connection.cursor()
    # 获取近一个月的时间
    lastmonth = (datetime.datetime.now() - relativedelta(months=1))
    sql = ''' select distinct name, user_risk from borrow, medicine 
    where borrow.medicine_id=medicine.id and boDate>=%s '''
    # cursor.execute执行SQL查询
    cursor.execute(sql, lastmonth)
    # 将cursor查询到的结果存储在变量data4
    data = cursor.fetchall()
    # 将data4列表转换为pandas的DataFrame对象
    data = pd.DataFrame(list(data), columns=['药品', '使用风险度'])
    # 关闭游标，释放与游标相关的资源。
    cursor.close()
    return data


# 添加浮点数计算模块
from decimal import Decimal


